/** @brief Fichier implémentant une classe qui gère la création d'un pixel.
 *
 * Ce fichier contient l'implémentation de la classe Capteur, qui permet de
 * générer un pixel.
 *
 * @date 2014/01/17
 * @author Johan "Solo" Luisier
 */
#include "Capteur.hpp"

#include "Camera.hpp"

#include "Materiaux/Melange.hpp"

#include "Utils/ATL.hpp"

#include <vector>

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

Capteur::Capteur( const unsigned& x, const unsigned& y, Camera* ptr )
    : PtrCamera( ptr )
{
    Coordonnees[ 0u ] = x;
    Coordonnees[ 1u ] = y;
}

Capteur::~Capteur()
{}

void Capteur::generePixel()
{
    if ( PtrCamera -> renduPrimaire() )
        generePixelPrimaire();
    else
        generePixelSecondaire();
}

void Capteur::generePixelPrimaire()
{
    double FocaleEff( PtrCamera -> distance() *
                      double( PtrCamera -> facteurOversampling() ) );
    VecteurU DirCopie( PtrCamera -> direction() );
    VecteurU HautCopie( PtrCamera -> hauteur() );
    VecteurU LargCopie( PtrCamera -> largeur() );
    Coord PosCopie( PtrCamera -> position() );

    unsigned i, j, k( 0u );

    int x, y;

    VecteurU dir;
    Rayon ray;

    /**
     * Le nombre de rayons tirés est égàl à @f$ f^2 @f$, avec @f$ f @f$ la
     * valeur de Camera::FacteurOversampling.
     *
     * @todo Vérifier que les coordonnées des points d'origine des rayons sont
     * bien correctes.
     */

    vector< Couleur > couleurs( puissance< 2u >( PtrCamera -> facteurOversampling() ) );
    vector< double > poids( puissance< 2u >( PtrCamera -> facteurOversampling() ),
                            1. / double( puissance< 2u >( PtrCamera -> facteurOversampling() ) ) );

    for ( i = 0u; i < PtrCamera -> facteurOversampling(); ++i )
    {
        for ( j = 0u; j < PtrCamera -> facteurOversampling(); ++j, ++k )
        {
            x = ( PtrCamera -> Film -> largeur() *
                  PtrCamera -> facteurOversampling() ) / 2 -
                    Coordonnees[ 0u ] * PtrCamera -> facteurOversampling() - j;
            y = ( PtrCamera -> Film -> hauteur() *
                  PtrCamera -> facteurOversampling() ) / 2 -
                    Coordonnees[ 1u ] * PtrCamera -> facteurOversampling() - i;

            dir = VecteurU( FocaleEff * DirCopie +
                            double( x ) * LargCopie +
                            double( y ) * HautCopie );

            ray = Rayon( PosCopie, dir );

            couleurs[ k ] = PtrCamera -> Env -> lanceRayon( ray,
                                                            PtrCamera -> precision(), 1.0 );
        }
    }

    PtrCamera -> Film -> setPixel( Coordonnees[ 1u ], Coordonnees[ 0u ],
            faireMelangeCouleurs( couleurs, poids ) );
}

void Capteur::generePixelSecondaire()
{
    if ( ! PtrCamera -> Contour[ Coordonnees[ 0u ] ][ Coordonnees[ 1u ] ] )
    {
        return;
    }

    Coord PosCopie( PtrCamera -> position() );


    vector< VecteurU > directions;
    Rayon ray;
    vector< Couleur > couleurs;

    vector< double > poids;

    if ( PtrCamera -> Anti  == simple )
        poids = list_of( .25 )( .25 )( .25 )( .25 );
    else if ( PtrCamera -> Anti == bourrin )
        poids = list_of( .2 )( .2 )( .2 )( .2 )( .2 );
    else if ( PtrCamera -> Anti == ultra_bourrin )
        poids = list_of( .1 )( .075 )( .075 )( .075 )( .075 )
                ( .075 )( .075 )( .075 )( .075 )( .075 )( .075 )( .075 )( .075 );
    else
        return;

    double x, y;

    unsigned k, largeur( PtrCamera -> Film -> largeur() ),
            hauteur( PtrCamera -> Film -> hauteur() ), nombre( poids.size() );

    x = static_cast< double >( largeur / 2 ) -
            static_cast< double >( Coordonnees[ 0u ] );
    y = static_cast< double >( hauteur / 2 ) -
            static_cast< double >( Coordonnees[ 1u ] );

    PtrCamera -> determinerDirections( directions, x, y );

    for ( k = 0; k < nombre; k++ )
    {
        ray = Rayon( PosCopie, directions[ k ] );
        couleurs.push_back( PtrCamera -> Env -> lanceRayon( ray,
                                                            PtrCamera -> precision(), 1.0 ) );
    }

    PtrCamera -> Compteur++;
    PtrCamera -> Film -> setPixel( Coordonnees[ 1u ], Coordonnees[ 0u ],
            faireMelangeCouleurs( couleurs, poids ) );
}
